package main

import (
	"bytes"
	"fmt"
	"io"
	"math/rand"
)

func main() {
	mFetcher := NewMysqlDataFetcher("mysql://127.0.0.1:3306")
	csvExporter := NewCsvExporter(mFetcher)
	var writer bytes.Buffer

	_ = csvExporter.Export("select * from xxx", &writer)

	oFetcher := NewOracleDataFetcher("mysql://127.0.0.1:1001")
	csvExporter.Fetcher(oFetcher)

	_ = csvExporter.Export("select * from xxx", &writer)

	// 从 MySQL 数据源导出 JSON
	jsonExporter := NewJsonExporter(mFetcher)
	_ = jsonExporter.Export("select * from xxx", &writer)
}

/*桥接模式适用于以下几种业务场景。

在抽象和具体实现之间需要增加更多灵活性的场景。
一个负责某块逻辑的类存在两个或多个独立变化的维度，而这些维度都需要独立进行扩展。
不希望使用继承，或因为多层继承导致系统类的个数剧增。

*/

type IDataFetcher interface {
	Fetch(sql string) []interface{}
}

type MysqlDataFetcher struct {
	Config string
}

func (mf *MysqlDataFetcher) Fetch(sql string) []interface{} {
	fmt.Println("Fetch data from mysql source: " + mf.Config)
	rows := make([]interface{}, 0)
	// 插入两个随机数组成的切片，模拟查询要返回的数据集
	rows = append(rows, rand.Perm(10), rand.Perm(10))
	return rows
}
func NewMysqlDataFetcher(configStr string) IDataFetcher {
	return &MysqlDataFetcher{
		Config: configStr,
	}
}

type OracleDataFetcher struct {
	Config string
}

func (of *OracleDataFetcher) Fetch(sql string) []interface{} {
	fmt.Println("Fetch data from oracle source: " + of.Config)
	rows := make([]interface{}, 0)
	// 插入两个随机数组成的切片，模拟查询要返回的数据集
	rows = append(rows, rand.Perm(10), rand.Perm(10))
	return rows
}

func NewOracleDataFetcher(configStr string) IDataFetcher {
	return &OracleDataFetcher{
		configStr,
	}
}

type IDataExporter interface {
	Fetcher(fetcher IDataFetcher)
	Export(sql string, writer io.Writer) error
}

type CsvExporter struct {
	mFetcher IDataFetcher
}

func (ce *CsvExporter) Fetcher(fetcher IDataFetcher) {
	ce.mFetcher = fetcher
}

func (ce *CsvExporter) Export(sql string, writer io.Writer) error {
	rows := ce.mFetcher.Fetch(sql)
	fmt.Printf("CsvExporter.Export, got %v rows\n", len(rows))
	for i, v := range rows {
		fmt.Printf("  行号: %d 值: %s\n", i+1, v)
	}
	return nil
}

func NewCsvExporter(fetcher IDataFetcher) IDataExporter {
	return &CsvExporter{
		fetcher,
	}
}

type JsonExporter struct {
	mFetcher IDataFetcher
}

func (je *JsonExporter) Fetcher(fetcher IDataFetcher) {
	je.mFetcher = fetcher
}

func (je *JsonExporter) Export(sql string, writer io.Writer) error {
	rows := je.mFetcher.Fetch(sql)
	fmt.Printf("JsonExporter.Export, got %v rows\n", len(rows))
	for i, v := range rows {
		fmt.Printf("  行号: %d 值: %s\n", i+1, v)
	}
	return nil
}

func NewJsonExporter(fetcher IDataFetcher) IDataExporter {
	return &JsonExporter{
		fetcher,
	}
}

/*
对于桥接模式而言，当不同的事物被联系到一起时，可以更换它们其中的任意一个而不受影响。
在上面的例子中，导出器是一个抽象维度，数据查询器是一个抽象维度。这两个抽象的实现类通过桥接的形式连接在一起，在这种情况下，
我们可以替换两个抽象维度中的实现类从而搭配出不同的组合，与此同时整体系统却不受到影响。
*/

/*最后我们再来总结一下桥接模式的优缺点以及正确使用它的难点。

桥接模式的优点

分离抽象部分及其具体实现部分。
提高了系统的扩展性，支持系统向两个或者多个维度的扩展。
符合开闭原则。
利用组合，大大提高了代码复用率。
桥接模式的缺点

增加了系统的理解与设计难度。
需要正确地识别系统中两个（或者多个）独立变化的维度，这一条也是桥接模式的难点。

*/
